#region The MIT License
// The MIT License
// 
// Copyright (c) 2009 octalforty studios
// 
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
// 
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
// 
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
#endregion
using System;
using System.Xml;
using octalforty.Componento.Shared.SemanticModel;
using octalforty.Componento.Shared.Util;

namespace octalforty.Componento.ComponentModel.Installation
{
    public class ComponentManifestParser
    {
        private readonly XmlNamespaceManager namespaceManager;

        public ComponentManifestParser()
        {
            namespaceManager = new XmlNamespaceManager(new NameTable());
            namespaceManager.AddNamespace("componento", "http://schemas.componento.net/1.0/manifest.xsd");
        }

        public ComponentManifest ParseComponentManifest(XmlNode xmlNode)
        {
            ComponentDescriptorParser componentDescriptorParser = new ComponentDescriptorParser();
            ComponentDescriptor descriptor = 
                componentDescriptorParser.ParseComponentDescriptor(
                    xmlNode.SelectSingleNode("componento:manifest/componento:component", namespaceManager), 
                    namespaceManager);

            Resource[] resources = ParseResources(xmlNode);
            PathMapping[] pathMappings = ParsePathMappings(xmlNode);
            ComponentReference[] dependencies = ParseDependencies(xmlNode);

            return new ComponentManifest(descriptor, resources, pathMappings, dependencies);
        }

        private ComponentReference[] ParseDependencies(XmlNode node)
        {
            return XmlParserUtil.Parse<ComponentReference>(node, "componento:manifest/componento:dependencies/componento:dependency", namespaceManager, 
                delegate(XmlNode xn)
                    {
                        string name = xn.Attributes["name"].Value;
                        Version version = XmlParserUtil.ParseAttributeValue<Version>(xn.Attributes["version"],
                            delegate(string s) { return new Version(s); });
                        Uri uri = XmlParserUtil.ParseUri(xn.Attributes["uri"]);

                        return new ComponentReference(name, version, uri);
                    });
        }

        private PathMapping[] ParsePathMappings(XmlNode xmlNode)
        {
            return XmlParserUtil.Parse<PathMapping>(xmlNode, "componento:manifest/componento:path-mappings/componento:path-mapping", namespaceManager, 
                delegate(XmlNode xn)
                    {
                        CliType? cli = XmlParserUtil.ParseEnum<CliType>(xn.Attributes["cli-name"]);
                        Version cliVersion = XmlParserUtil.ParseAttributeValue<Version>(
                            xn.Attributes["cli-version"],
                            delegate(string s) { return new Version(s); });
                        IsaType isa = XmlParserUtil.ParseEnum<IsaType>(xn.Attributes["isa"]) ?? IsaType.Cil;

                        CliEnvironment cliEnvironment =
                            cli.HasValue ?
                                new CliEnvironment(cli.Value, cliVersion) :
                                null;

                        PathMapping pathMapping = new PathMapping(cliEnvironment, isa);

                        foreach(XmlNode mapping in xn.ChildNodes)
                        {
                            pathMapping.AddPathMap(
                                (PathMappingTarget)Enum.Parse(typeof(PathMappingTarget), mapping.LocalName, true),
                                mapping.InnerText);
                        } // foreach

                        return pathMapping;
                    });
        }

        private Resource[] ParseResources(XmlNode xmlNode)
        {
            return XmlParserUtil.Parse<Resource>(xmlNode, "componento:manifest/componento:resources/componento:resource", namespaceManager, 
                delegate(XmlNode xn)
                    {
                        Uri resourceUri = XmlParserUtil.ParseUri(xn.Attributes["uri"]);
                        int? length = XmlParserUtil.ParseAttributeValue<int?>(
                            xn.Attributes["length"],
                            delegate(string s) { return Convert.ToInt32(s); });

                        return length.HasValue ? 
                            new Resource(resourceUri, length.Value) :
                            new Resource(resourceUri);
                    });
        }
    }
}
